package edu.gmu.atelier.generic;

import edu.gmu.atelier.Epsilon;

/**
 * Matrix implementation such that each col has exactly one component with
 * the value of 1.0 and 0.0 every other component.  The internal structure
 * is memory efficient in the sense that only O(n) memory is required
 * versus a more naive O(n^2) approach.  It is also somewhat performance
 * efficient in that it avoids multiplying by 1.0 and 0.0.
 * @author James H. Pope
 */
public class PermutationMatrix <T extends Vector> //implements Matrix<T>
{
    /*          row
     *         0 0 0 1  1 2 3 4       d e f g
     * goes    1 0 0 0  5 6 7 8  -->  1 2 3 4
     *  to     0 1 0 0  9 a b c       5 6 7 8
     *  row    0 0 1 0  d e f g       9 a b c
     *        {1,2,3,0}
     * 
     *         0 1 0 0  d e f g       1 2 3 4
     * goes    0 0 1 0  1 2 3 4  -->  5 6 7 8
     *  to     0 0 0 1  5 6 7 8       9 a b c
     *  row    1 0 0 0  9 a b c       d e f g
     *        {3,0,1,2}
     * 
     *         0 1 0 0  0 0 0 1       1 0 0 0
     * goes    0 0 1 0  1 0 0 0  -->  0 1 0 0
     *  to     0 0 0 1  0 1 0 0       0 0 1 0
     *  row    1 0 0 0  0 0 1 0       0 0 0 1
     *                               {0,1,2,3}
     * 
     * P^T = P^-1  ==>  P^T PA = IA = A
     */
    public int[] perms = null;
    
    public PermutationMatrix( int[] perms )
    {
        this.perms = perms;
    }
    
    public int getCols()
    {
        return this.perms.length;
    }
    
    public int getRows()
    {
        return this.perms.length;
    }
    
    
    //------------------------------------------------------------------------//
    // Interface Methods
    //------------------------------------------------------------------------//
    public Vector mult(Vector x)
    {
        Vector b = x.make();
        for (int i = 0; i < perms.length; i++)
        {
//            b[perms[i]] = x.getComponent(i);
        }
        //System.out.println( "Where go wrong? " + Arrays.toString( b ) );
        return b;
//        double[] b = new double[perms.length];
//        for (int i = 0; i < perms.length; i++)
//        {
//            b[perms[i]] = x.getComponent(i);
//        }
//        //System.out.println( "Where go wrong? " + Arrays.toString( b ) );
//        RealVector bv = new RealVector(b);
//        return bv;
    }
    
//    public Matrix<T> mult(double scalar)
//    {
//        String s = "Cannot scalar multiply a permutation matrix.";
//        throw new UnsupportedOperationException(s);
//    }
    
    public Matrix<T> mult(Matrix<T> b)
    {
        String s = "Cannot scalar multiply a permutation matrix.";
        throw new UnsupportedOperationException(s);
    }
    
    public PermutationMatrix<T> transpose()
    {
        return transpose( this );
    }
    
    public RealVector getVector(int j)
    {
        double[] col = new double[this.perms.length];
        col[ perms[j] ] = 1.0;
        return new RealVector(col);
    }
    
    //------------------------------------------------------------------------//
    // Class instance methods
    //------------------------------------------------------------------------//
    public boolean similar( Object obj, Epsilon e )
    {
        if(obj == this)
        {
            return true;
        }
        
        if( (obj instanceof PermutationMatrix) == false )
        {
            return false;
        }
        
        PermutationMatrix o = (PermutationMatrix)obj;
        
        if( o.getCols() != this.getCols() ||
            o.getRows() != this.getRows() )
        {
            return false;
        }
        
        for (int i = 0; i < this.perms.length; i++)
        {
            if( perms[i] != o.perms[i] )
            {
                return false;
            }
        }
        
        return true;
    }
    
    public boolean equals( Object obj )
    {
        return this.similar(obj, null);
    }
    
    public String toString()
    {
        //System.out.println( "Where go wrong? " + Arrays.toString( perms ) );
        StringBuffer buf = new StringBuffer("[");
        for (int i = 0; i < perms.length; i++)
        {
            buf.append( "\nv"+i+"=");
            for (int j = 0; j < perms.length; j++)
            {
                if( i == perms[j] ) buf.append( "1," );
                else                buf.append( "0," );
            }
        }
        buf.append("]");
        return buf.toString();
    }
    
    //------------------------------------------------------------------------//
    // Static convenience methods
    //------------------------------------------------------------------------//
    public static RealVector mult(PermutationMatrix p, RealVector x)
    {
        //System.out.println( "P " + Arrays.toString( p.perms ) );
        double[] b = new double[p.perms.length];
        for (int i = 0; i < p.perms.length; i++)
        {
            b[p.perms[i]] = x.elements[i];
        }
        //System.out.println( "Where go wrong? " + Arrays.toString( b ) );
        RealVector bv = new RealVector(b);
        return bv;
    }
    
    
    public static PermutationMatrix transpose(PermutationMatrix a)
    {
        int[] c = new int[a.perms.length];
        for (int i = 0; i < c.length; i++)
        {
            for (int j = 0; j < c.length; j++)
            {
                if( a.perms[j] == i )
                {
                    c[i] = j;
                    break;
                }
            }
        }
        return new PermutationMatrix(c);
    }
    
    //------------------------------------------------------------------------//
    // double[][] utilities
    //------------------------------------------------------------------------//
    public static PermutationMatrix make(double[][] data)
    {
        int[] perms = new int[data.length];
        for (int j = 0; j < perms.length; j++)
        {
            for (int i = 0; i < perms.length; i++)
            {
                if( data[i][j] == 1.0 )
                {
                    perms[j] = i;
                }
            }
        }
        return new PermutationMatrix( perms );
    }
    
    public static int[] makeCopy(PermutationMatrix a)
    {
        int[] c = new int[a.perms.length];
        for (int i = 0; i < c.length; i++)
        {
            c[i] = a.perms[i];
        }
        return c;
    }
}
